from rest_framework import serializers

from goods.models import SKU


class CartSKUSerializer(serializers.ModelSerializer):
    """
    获取所有选中购物车信息
    """
    count = serializers.IntegerField(label='数量')

    class Meta:
        model = SKU
        fields = ['id', 'name', 'default_image_url', 'price', 'count']


class OrderSettlementSerializer(serializers.Serializer):
    """
    订单结算序列化器，定义运费
    """
    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)
    skus = CartSKUSerializer(many=True)


from orders.models import OrderInfo
from django.utils import timezone
from decimal import Decimal
from django_redis import get_redis_connection
from .models import OrderGoods
from django.db import transaction


class OrderCommitSerializer(serializers.ModelSerializer):
    """
    生成订单序列化器
    """

    class Meta:
        model = OrderInfo
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        extra_kwargs = {
            'address': {
                'write_only': True,
                'required': True,
            },
            'pay_method': {
                'write_only': True,
                'required': True
            }
        }

    def create(self, validated_data):
        """
        validated_data 只要三个数据　用户　地址　支付方式
        系统的create只保存这三个数据，不能满足需求，所有我们需要重写create方法

        需要先生成订单信息，在生成订单商品信息
            因为订单商品信息需要订单id

        生成订单
            获取user信息
            获取地址信息
            生成一个订单号　
            运费　价格　数量
            支付方式
            状态
        生成订单商品信息
            redis
                hash sku_id:count
                set  sku_id
            链接redis，获取redis数据
            获取选择商品的信息
            根据id获取商品信息
            对商品信息进行遍历
                修改商品的的库存和销量
                需要计算商品价格和数量
                保存商品
        """
        # 生成订单
        #     获取user信息
        user = self.context['request'].user
        #     获取地址信息
        address = validated_data.get('address')
        #     生成一个订单号　时间　＋　id的订单号
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        #    运费，价格，数量，　可以先初始化，等保存订单的时候在计算
        # Decimal  是一个货比类型
        #  数量
        total_count = 0
        #  价格
        total_amount = Decimal('0')
        #  运费
        freight = Decimal('10')
        #     支付方式
        pay_method = validated_data.get('pay_method')
        #     订单状态 会因为选择的方式不同
        if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH']:
            #  货到付款，代发货
            status = OrderInfo.PAY_METHODS_ENUM['ALIPAY']
        else:
            #  支付宝支付，待付款
            status = OrderInfo.PAY_METHODS_ENUM['CASH']
        #  开启事物
        with transaction.atomic():
            #  创建一个保存点
            point = transaction.savepoint()
            try:
                # 保存订单到数据库
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=total_count,
                    total_amount=total_amount,
                    freight=freight,
                    pay_method=pay_method,
                    status=status
                )
            except Exception:
                transaction.savepoint_rollback(point)
                raise serializers.ValidationError('保存失败')

            # 生成订单商品信息
            #     redis
            #         hash sku_id:count
            #         set  sku_id
            #     链接redis，获取redis数据
            redis_conn = get_redis_connection('carts')
            #  hash数据
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            #  set数据
            redis_selected = redis_conn.smembers('cart_selected_%s' % user.id)
            #     获取选择商品的信息
            cart_dict = {}
            for sku_id in redis_selected:
                cart_dict[int(sku_id)] = int(redis_cart[sku_id])
            # 根据id获取商品信息
            skus = SKU.objects.filter(id__in=cart_dict.keys())
            #     对商品信息进行遍历
            for sku in skus:
                #  获取订单商品的数量
                count = cart_dict[sku.id]
                #  判断库存数量
                if count > sku.stock:
                    #  出问题的时候回滚
                    #  回滚到事物的保存点
                    transaction.savepoint_rollback(point)
                    raise serializers.ValidationError('库存不足')
                # 修改商品的的库存和销量
                # sku.stock -= count
                # sku.sales += count
                # sku.save()

                #  在高并发的时候(多个用户同时访问)容易造成资源竞争问题，导致库存数量异常
                #  可以使用悲观锁(就是当一个线程访问的时候，让数据库为该记录加锁，拒绝别人进行访问，但是在多线程中，这样做容易造成死锁的问题)
                #  我们使用乐观锁　乐观锁并不是真的锁
                #  是我们先记录查询之前的库存，在修改的时候再次查询数据库存，与之前的库存进行对比，如果一致进行修改操作，不易真则不进行修改操作
                #  先记录库存
                # import time
                # time.sleep(7)
                old_stock = sku.stock
                #  最终要更新数据，我们先把要更新的数据准备号
                new_stock = sku.stock - count
                new_sales = sku.sales + count
                #  我们修改数据的时候在查询一次
                result = SKU.objects.filter(pk=sku.id, stock=old_stock).update(stock=new_stock, sales=new_sales)
                #  判断是否修改成功
                if result == 0:
                    raise serializers.ValidationError('下单失败')
                # 需要计算商品价格和数量
                order.total_count += count
                order.total_amount += sku.price * count
                #         保存商品
                try:
                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=count,
                        price=sku.price
                    )
                except Exception:
                    transaction.savepoint_rollback(point)
                    raise serializers.ValidationError('保存失败')
            # 对商品的总价和数量进行了修改，所有要保存
            order.save()
            #  提交事物
            transaction.savepoint_commit(point)
            #  更新redis中保存的数据
            p = redis_conn.pipeline()
            p.hdel('cart_%s' % user.id, *redis_selected)
            p.srem('cart_selected_%s' % user.id, *redis_selected)
            p.execute()
            return order
        return validated_data
